#ifndef SNES_PPU_H
#define SNES_PPU_H

#pragma once

#include <array>
#include <vector>
#include <functional>

#include "PictureBus.h"
#include "MainBus.h"
#include "VirtualScreen.h"
#include "PaletteColors.h"

namespace snes {

static const int ScanlineCycleLength = 341;
static const int ScanlineEndCycle = 340;
static const int VisibleScanlines = 240;
static const int ScanlineVisibleDots = 256;
static const int FrameEndScanline = 261;

static const int AttributeOffset = 0x3C0;

class PPU
{
public:
    PPU(PictureBus &bus, VirtualScreen &screen);

    void step();
    void reset();

    void setInterruptCallback(std::function<void(void)> cb);

    void doDMA(const Byte* page_ptr);

    // Callbacks mapped to CPU address space
    // Addresses written to by the program
    void control(Byte ctrl);
    void setMask(Byte mask);
    void setOAMAddress(Byte addr);
    void setDataAddress(Byte addr);
    void setScroll(Byte scroll);
    void setData(Byte data);

    // Read by the program
    Byte getStatus();
    Byte getData();
    Byte getOAMData();
    void setOAMData(Byte value);

private:
    Byte readOAM(Byte addr);
    void writeOAM(Byte addr, Byte value);
    Byte read(Address addr);
    PictureBus &m_bus;
    VirtualScreen &m_screen;

    std::function<void(void)> m_vblankCallback;

    std::vector<Byte> m_spriteMemory;

    std::vector<Byte> m_scanlineSprites;

    enum State
    {
        PreRender,
        Render,
        PostRender,
        VerticalBlank
    };

    State m_pipelineState;

    int m_cycle;
    int m_scanline;
    bool m_evenFrame;

    bool m_vblank;
    bool m_sprZeroHit;
    bool m_spriteOverflow;

    // Registers
    Address m_dataAddress;
    Address m_tempAddress;
    Byte m_fineXScroll;
    bool m_firstWrite;
    Byte m_dataBuffer;

    Byte m_spriteDataAddress;

    // Setup flags and variables
    bool m_longSprites;
    bool m_generateInterrupt;

    bool m_greyscaleMode;
    bool m_showSprites;
    bool m_showBackground;
    bool m_hideEdgeSprites;
    bool m_hideEdgeBackground;

    enum CharacterPage
    {
        Low,
        High,
    };
    
    CharacterPage m_bgPage;
    CharacterPage m_sprPage;

    Address m_dataAddrIncrement;

    std::vector<std::vector<sf::Color>> m_pictureBuffer;
};

} // namespace snes

#endif // SNES_PPU_H
